import { AwakeSystem } from "../../../../../Core/Module/EventSystem/IAwakeSystem";
import { DestroySystem } from "../../../../../Core/Module/EventSystem/IDestroySystem";
import { Log } from "../../../../../Core/Module/Log/Log";
import { ErrorCore } from "../../../../../Core/Module/Network/ErrorCore";
import { Options } from "../../../../../Core/Module/Options/Options";
import { TimeHelper } from "../../../../../Core/Module/Time/TimeHelper";
import { ATimer } from "../../../../../Core/Module/Timer/ATimer";
import { TimerComponent } from "../../../../../Core/Module/Timer/TimerComponent";
import { Exception } from "../../../../../Core/Object/Exception";
import { SerializeHelper } from "../../../../../Core/Serialize/SerializeHelper";
import { ExpandOf, FriendOf, InvokeAttribute, ObjectSystemAttribute } from "../../../../../Decorator/Decorator";
import { ETTask } from "../../../../../Support/Share/Lib/Async/ETTask";
import { ActorMessageSender } from "../../../../Model/Server/Module/Actor/ActorMessageSender";
import { ActorMessageSenderComponent } from "../../../../Model/Server/Module/Actor/ActorMessageSenderComponent";
import { NetInnerComponent, ProcessActorId } from "../../../../Model/Server/Module/Message/NetInnerComponent";
import { IActorRequest, IActorResponse } from "../../../../Model/Share/Module/Actor/IActorMessage";
import { IMessage } from "../../../../Model/Share/Module/Message/IMessage";
import { TimerInvokeType } from "../../../../Model/Share/TimerInvokeType";
import { ActorHelper } from "./ActorHelper";

export class ActorMessageSenderSystem {

    public static Run(self: ActorMessageSender, response: IActorResponse): void {
        // console.log(`ActorMessageSenderComponentSystem.Run`);

        if (response.Error == ErrorCore.ERR_ActorTimeout) {
            self.Tcs.SetException(new Exception(`Rpc error: request, 注意Actor消息超时，请注意查看是否死锁或者没有reply: actorId: ${self.ActorId} ${self.Request}, response: ${response}`));
            return;
        }

        if (self.NeedException && ErrorCore.IsRpcNeedThrowException(response.Error)) {
            self.Tcs.SetException(new Exception(`Rpc error: actorId: ${self.ActorId} request: ${self.Request}, response: ${response}`));
            return;
        }

        self.Tcs.SetResult(response);
    }

}

/**
 * 使用 Expand 扩展方法
 * 
 * 必须作出以下申明，才能调用对象的扩展方法
 */
declare module "../../../../Model/Server/Module/Actor/ActorMessageSenderComponent" {

    interface ActorMessageSenderComponent {

        Check(): void;

        Send(actorId: number, message: IMessage): Promise<void>;

        GetRpcId(): number;

        Call(actorId: number, request: IActorRequest, needException?: boolean): Promise<IActorResponse>;

        CallRpc(actorId: number, rpcId: number, iActorRequest: IActorRequest, needException?: boolean): Promise<IActorResponse>;

        HandleIActorResponse(response: IActorResponse): void;

    }

}

@ExpandOf(ActorMessageSenderComponent)
export class ActorMessageSenderComponentSystem {

    private static Check(self: ActorMessageSenderComponent): void {
        // console.log(`ActorMessageSenderComponent.Check`);

        let timeNow = TimeHelper.ServerNow();
        for (let key in self.requestCallback.Keys) {
            let rpcId = parseInt(key);
            let value = self.requestCallback.TryGetValue(rpcId);
            // 因为是顺序发送的，所以，检测到第一个不超时的就退出
            if (timeNow < value.CreateTime + ActorMessageSenderComponent.TIMEOUT_TIME) {
                break;
            }
            self.TimeoutActorMessageSenders.Add(rpcId);
        }

        for (let key in self.TimeoutActorMessageSenders) {
            let rpcId = parseInt(key);
            let actorMessageSender = self.requestCallback.Get(rpcId);
            self.requestCallback.Remove(rpcId);
            try {
                let response = ActorHelper.CreateResponse(actorMessageSender.Request, ErrorCore.ERR_ActorTimeout);
                ActorMessageSenderSystem.Run(actorMessageSender, response);
            } catch (e) {
                Log.Error(e.toString());
            }
        }
        self.TimeoutActorMessageSenders.Clear();
    }

    private static async Send(self: ActorMessageSenderComponent, actorId: number, message: IMessage): Promise<void> {
        // console.log(`ActorMessageSenderComponent.Send`);

        if (actorId == 0) {
            throw new Exception(`actor id is 0: ${message}`);
        }
        let processActorId = new ProcessActorId(actorId);
        // 这里做了优化，如果发向同一个进程，则直接处理，不需要通过网络层
        if (processActorId.Process == Options.Instance.Process) {
            NetInnerComponent.Instance.HandleMessage(actorId, SerializeHelper.WrapMessage(message));
            return;
        }
        let session = await NetInnerComponent.Instance.Get(processActorId.Process);
        session.SendTo(processActorId.ActorId, message);
    }

    private static GetRpcId(self: ActorMessageSenderComponent): number {
        return ++self.RpcId;
    }

    private static async Call(
        self: ActorMessageSenderComponent,
        actorId: number,
        request: IActorRequest,
        needException: boolean = true
    ): Promise<IActorResponse> {
        // console.log(`ActorMessageSenderComponent.Call`);

        request.RpcId = self.GetRpcId();

        if (actorId == 0) {
            throw new Exception(`actor id is 0: {request}`);
        }

        return await self.CallRpc(actorId, request.RpcId, request, needException);
    }

    private static async CallRpc(
        self: ActorMessageSenderComponent,
        actorId: number,
        rpcId: number,
        iActorRequest: IActorRequest,
        needException: boolean = true
    ): Promise<IActorResponse> {
        // console.log(`ActorMessageSenderComponent.CallWithRpcId`);

        if (actorId == 0) {
            throw new Exception(`actor id is 0: ${iActorRequest}`);
        }

        let tcs = ETTask.Create<IActorResponse>();

        self.requestCallback.Add(rpcId, new ActorMessageSender(actorId, iActorRequest, tcs, needException));

        self.Send(actorId, iActorRequest);

        let beginTime = TimeHelper.ServerFrameTime();
        let response = await tcs;
        let endTime = TimeHelper.ServerFrameTime();

        let costTime = endTime - beginTime;
        if (costTime > 200) {
            Log.Warning(`actor rpc time > 200: ${costTime} ${iActorRequest}`);
        }

        return response;
    }

    private static HandleIActorResponse(self: ActorMessageSenderComponent, response: IActorResponse): void {
        // console.log(`ActorMessageSenderComponent.HandleIActorResponse`);

        let actorMessageSender = self.requestCallback.TryGetValue(response.RpcId);
        if (!actorMessageSender) {
            return;
        }

        self.requestCallback.Remove(response.RpcId);

        ActorMessageSenderSystem.Run(actorMessageSender, response);
    }

}

@InvokeAttribute(TimerInvokeType.ActorMessageSenderChecker)
export class ActorMessageSenderChecker extends ATimer<ActorMessageSenderComponent>
{

    protected override Run(self: ActorMessageSenderComponent): void {
        try {
            self.Check();
        } catch (e) {
            Log.Error(`move timer error: ${self.Id}\n${e}`);
        }
    }
    
}

@FriendOf(ActorMessageSenderComponent)
@ObjectSystemAttribute()
export class ActorMessageSenderComponentAwakeSystem extends AwakeSystem<ActorMessageSenderComponent>
{
    Awake(self: ActorMessageSenderComponent): void {
        ActorMessageSenderComponent.Instance = self;

        self.TimeoutCheckTimer = TimerComponent.Instance.NewRepeatedTimer(1000, TimerInvokeType.ActorMessageSenderChecker, self);
    }
}

@FriendOf(ActorMessageSenderComponent)
@ObjectSystemAttribute()
export class ActorMessageSenderComponentDestroySystem extends DestroySystem<ActorMessageSenderComponent>
{
    Destroy(self: ActorMessageSenderComponent): void {
        ActorMessageSenderComponent.Instance = null;
        TimerComponent.Instance?.Remove(self.TimeoutCheckTimer);
        self.TimeoutCheckTimer = 0;
        self.TimeoutActorMessageSenders.Clear();
    }

}